#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>

#include "serveur.h"

void winstart(void)
{
#ifdef WIN32
	WSADATA wsa;
	int err = WSAStartup(MAKEWORD(2, 2), &wsa);
	if(err < 0)
	{
		puts("WSAStartup failed !");
		exit(EXIT_FAILURE);
	}
#endif
}

void winstop(void)
{
#ifdef WIN32
	WSACleanup();
#endif
}

void cerveau(void)
{
	SOCKET sock = debutConnexion();
	char buffer[TAILLE_BUFFER];
	/* the index for the array */
	int nbClients = 0;
	int max = sock;
	/* an array for all clients */
	Client clients[MAX_CLIENTS];

	fd_set rdfs;

	while(1)
	{
		int i = 0;

		/**  */
		FD_ZERO(&rdfs);

		/* add STDIN_FILENO */
		FD_SET(STDIN_FILENO, &rdfs);

		/* add the connection socket */
		FD_SET(sock, &rdfs);

		/* add socket of each client */
		for(i = 0; i < nbClients; i++)
		{
			FD_SET(clients[i].sock, &rdfs);
		}
		/** attwinstop qu'un socket soit utilisé */
		if(select(max + 1, &rdfs, NULL, NULL, NULL) == -1)
		{
			perror("select()");
			exit(errno);
		}

		/* something from standard input : i.e keyboard */
		if(FD_ISSET(STDIN_FILENO, &rdfs))
		{
			/* stop process when type on keyboard */
			break;
		}
		/* si 
		 */
		else if(FD_ISSET(sock, &rdfs))
		{
			/* new client */
			SOCKADDR_IN csin = { 0 };
			socklen_t sinsize = sizeof csin;
			int csock = accept(sock, (SOCKADDR *)&csin, &sinsize);
			if(csock == SOCKET_ERROR)
			{
				perror("accept()");
				continue;
			}

			/* after connecting the client swinstops its name */
			if(lireTexte(csock, buffer) == -1)
			{
				/* disconnected */
				continue;
			}

			/* what is the new maximum fd ? */
			max = csock > max ? csock : max;

			FD_SET(csock, &rdfs);

			Client c = { csock };
			strncpy(c.name, buffer, TAILLE_BUFFER - 1);
			clients[nbClients] = c;
			nbClients++;
		}
		else
		{
			int i = 0;
			for(i = 0; i < nbClients; i++)
			{
				/* a client is talking */
				if(FD_ISSET(clients[i].sock, &rdfs))
				{
					Client client = clients[i];
					int c = lireTexte(clients[i].sock, buffer);
					/* client disconnected */
					if(c == 0)
					{
						closesocket(clients[i].sock);
						supprimerClient(clients, i, &nbClients);
						strncpy(buffer, client.name, TAILLE_BUFFER - 1);
						strncat(buffer, " disconnected !", TAILLE_BUFFER - strlen(buffer) - 1);
						diffuserMessage(clients, client, nbClients, buffer, 1);
					}
					else
					{
						diffuserMessage(clients, client, nbClients, buffer, 0);
					}
					break;
				}
			}
		}
	}

	supprimerClients(clients, nbClients);
	finConnexion(sock);
}

void supprimerClients(Client *clients, int nbClients)
{
	int i = 0;
	for(i = 0; i < nbClients; i++)
	{
		closesocket(clients[i].sock);
	}
}

void supprimerClient(Client *clients, int to_remove, int *nbClients)
{
	/* we remove the client in the array */
	memmove(clients + to_remove, clients + to_remove + 1, (*nbClients - to_remove - 1) * sizeof(Client));
	/* number client - 1 */
	(*nbClients)--;
}

void diffuserMessage(Client *clients, Client swinstoper, int nbClients, const char *buffer, char from_server)
{
	int i = 0;
	char message[TAILLE_BUFFER];
	message[0] = 0;
	for(i = 0; i < nbClients; i++)
	{
		/* we don't swinstop message to the swinstoper */
		if(swinstoper.sock != clients[i].sock)
		{
			if(from_server == 0)
			{
				strncpy(message, swinstoper.name, TAILLE_BUFFER - 1);
				strncat(message, " : ", sizeof message - strlen(message) - 1);
			}
			strncat(message, buffer, sizeof message - strlen(message) - 1);
			ecrireTexte(clients[i].sock, message);
		}
	}
}

int debutConnexion(void)
{
	SOCKET sock = socket(AF_INET, SOCK_STREAM, 0);
	SOCKADDR_IN sin = { 0 };

	if(sock == INVALID_SOCKET)
	{
		perror("socket()");
		exit(errno);
	}

	sin.sin_addr.s_addr = htonl(INADDR_ANY);
	sin.sin_port = htons(PORT);
	sin.sin_family = AF_INET;

	if(bind(sock,(SOCKADDR *) &sin, sizeof sin) == SOCKET_ERROR)
	{
		perror("bind()");
		exit(errno);
	}

	if(listen(sock, MAX_CLIENTS) == SOCKET_ERROR)
	{
		perror("listen()");
		exit(errno);
	}

	return sock;
}

void finConnexion(int sock)
{
	closesocket(sock);
}

int lireTexte(SOCKET sock, char *buffer)
{
	int n = 0;

	if((n = recv(sock, buffer, TAILLE_BUFFER - 1, 0)) < 0)
	{
		perror("recv()");
		/* if recv error we disonnect the client */
		n = 0;
	}

	buffer[n] = 0;

	return n;
}

void ecrireTexte(SOCKET sock, const char *buffer)
{
	if(send(sock, buffer, strlen(buffer), 0) < 0)
	{
		perror("swinstop()");
		exit(errno);
	}
}
/*
   int lireTexte_bin(SOCKET sock, void *buffer)
   {

   }

   void ecrireTexte_bin(SOCKET sock, void * buffer)
   {

   }

 */
